package com.black.cat.jvm.classfile;

import com.black.cat.jvm.classfile.attribute.AttributeInfo;
import com.black.cat.jvm.classfile.attribute.BaseAttributeInfo;
import com.black.cat.jvm.classfile.constantpool.ConstantPool;
import com.black.cat.jvm.common.CommonValue;
import lombok.Data;

/**
 * @Author blackcat
 * @create 2021/8/12 8:25
 * @version: 1.0
 * @description: class 文件
 *
 *ClassFile {
 * u4 magic; 魔数(0xCAFEBABE)
 * u2 minor_version;次版本号
 * u2 major_version;主版本号
 * u2 constant_pool_count;常量计数器
 * cp_info constant_pool[constant_pool_count-1];常量池
 * u2 access_flags;访问标志
 * u2 this_class;类索引
 * u2 super_class;父类索引
 * u2 interfaces_count;接口计数器
 * u2 interfaces[interfaces_count];接口表
 * u2 fields_count;字段计数器
 * field_info fields[fields_count];字段表
 * u2 methods_count;方法计数器
 * method_info methods[methods_count];方法表
 * u2 attributes_count;属性计数器
 * attribute_info attributes[attributes_count];属性表
 * }
 *
 */
@Data
public class ClassFile {


    private ClassReader reader;

    private String magic;
    private int minorVersion;
    private int majorVersion;
    //    private int constantPoolCount;
    private ConstantPool constantPool;
    private int accessFlag;
    private int classNameIndex;
    private int superClassNameIndex;
    //    private int interfaceCount;
    private int[] interfaceIndexes;
    //    private int fieldCount;
    private MemberInfo[] fieldInfos;
    //    private int methodCount;
    private MemberInfo[] methodInfos;
    //    private int attributeCount;
    private AttributeInfo[] attributeInfos;


    public ClassFile(byte[] bytes) {
        this.reader = new ClassReader(bytes);
        readAndCheckMagic();
        readAndCheckVersion();
        readConstantPool();
        readAccessFlag();
        readClassNameIndex();
        readSuperClassNameIndex();
        readInterfaceIndexes();
        readFields();
        readMethods();
        readAttributes();
    }

    private void readAndCheckMagic() {
        String magic = reader.nextU4ToHexString();
        this.magic = magic;
        if (!"cafebabe".equalsIgnoreCase(magic)) {
            throw new ClassFormatError("magic!");
        }
    }

    private void readAndCheckVersion() {
        this.minorVersion = this.reader.nextU2ToInt();
        this.majorVersion = this.reader.nextU2ToInt();
        // jdk11对应的major为55，支持jdk1.2-jdk11
        if (this.majorVersion >= CommonValue.JDK_VERSION_MAJOR_02
                && this.majorVersion <= CommonValue.JDK_VERSION_MAJOR_11
                && this.minorVersion == CommonValue.JDK_VERSION_MINOR_00) {
            return;
        }
        throw new UnsupportedClassVersionError();
    }

    private void readConstantPool() {
        this.constantPool = new ConstantPool(this.reader);
    }

    private void readAccessFlag() {
        this.accessFlag = this.reader.nextU2ToInt();
    }

    private void readClassNameIndex() {
        this.classNameIndex = this.reader.nextU2ToInt();
    }

    private void readSuperClassNameIndex() {
        this.superClassNameIndex = this.reader.nextU2ToInt();
    }

    private void readInterfaceIndexes() {
        this.interfaceIndexes = reader.nextUint16s();
    }


    private void readFields() {
        fieldInfos = MemberInfo.readMembers(constantPool, reader);
    }

    private void readMethods() {
        methodInfos = MemberInfo.readMembers(constantPool, reader);
    }

    private void readAttributes() {
        this.attributeInfos = BaseAttributeInfo.readAttributes(reader, constantPool);
    }
}
